/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.quickorm.entity;

import com.quickorm.dialect.Dialect;
import com.quickorm.stereotype.Column;
import com.quickorm.stereotype.Id;
import com.quickorm.stereotype.NotColumn;
import com.quickorm.stereotype.Table;
import com.quickorm.helpers.ObjectHelper;
import com.quickorm.helpers.StringUtils;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 *
 * @author aaa
 */
public class EntityMetaData {

    private String tableName;
    private Class entityClass;
    private String primaryKey;
    private Field primaryKeyField;
    private Map<String, Field> columnNameFieldMap;
    private Map<String, Field> fieldNameFieldMap;

    /**
     * @return 获取实体对应的表名
     */
    public String getTableName() {
        return tableName;
    }

    /**
     * @return 获取实体类
     */
    public Class getEntityClass() {
        return entityClass;
    }

    /**
     * @return 获取实体对应表的主键
     */
    public String getPrimaryKey() {
        return primaryKey;
    }

    /**
     * @return 获取主键字段
     */
    public Field getPrimaryKeyField() {
        return primaryKeyField;
    }

    /**
     * @return 获取列名字段Map
     */
    public Map<String, Field> getColumnNameFieldMap() {
        return columnNameFieldMap;
    }

    /**
     * 得到主键的值
     * @param t
     * @return 
     */
    public Object getPrimaryKeyValue(Object t) {
        try {
            return this.getPrimaryKeyField().get(t);
        } catch (Exception ex) {
            return null;
        }
    }

    public EntityMetaData(Class entityClass) {
        //初始化
        columnNameFieldMap = new LinkedHashMap<String, Field>();
        fieldNameFieldMap = new LinkedHashMap<String, Field>();

        this.entityClass = entityClass;
        //扫描entityClass(注意，要设置Accessable为true)

        //首先取得表名
        Table tableAnnotation = (Table) entityClass.getAnnotation(Table.class);
        if (tableAnnotation == null || StringUtils.isEmpty(tableAnnotation.name())) {
            this.tableName = entityClass.getSimpleName();
        } else {
            this.tableName = tableAnnotation.name();
        }
        //然后扫描字段
        Field[] filedArray = ObjectHelper.getClassAllField(entityClass);
        for (int i = 0; i <= filedArray.length - 1; i++) {
            Field field = filedArray[i];
            //设置为可访问
            field.setAccessible(true);

            String tmpColumnName = tmpColumnName = field.getName();
            //添加到所有字段名与字段Map中
            fieldNameFieldMap.put(tmpColumnName, field);
            //如果有"NotColumn"注解，则说明此字段不是数据列
            if (field.getAnnotation(NotColumn.class) != null) {
                continue;
            }

            Column columnAnnotation = field.getAnnotation(Column.class);
            //如果没有"Column"注解，或者没有设置列名
            if (columnAnnotation == null || StringUtils.isEmpty(columnAnnotation.name())) {
                //如果字段是protected修饰，则说明此字段不是数据列
                if (Modifier.isProtected(field.getModifiers())) {
                    continue;
                }
                tmpColumnName = field.getName();
            } else {
                tmpColumnName = columnAnnotation.name();
            }
            //字段加入Map中
            columnNameFieldMap.put(tmpColumnName, field);

            //判断是不是主键
            if (field.getAnnotation(Id.class) != null) {
                this.primaryKey = tmpColumnName;
                this.primaryKeyField = field;
            }
        }

        //如果没有找到主键，则默认第一个字段为主键
        if (this.primaryKey == null) {
            this.primaryKey = columnNameFieldMap.keySet().iterator().next();
            this.primaryKeyField = columnNameFieldMap.get(primaryKey);
        }
    }

    //根据数据库字段名得到Field
    public Field getFieldByDataBaseColumnName(String columnName) {
        //如果columnNameFieldMap中包括此columnName
        if (columnNameFieldMap.containsKey(columnName)) {
            return columnNameFieldMap.get(columnName);
        } else if (fieldNameFieldMap.containsKey(columnName)) {
            return fieldNameFieldMap.get(columnName);
        } else {
            //将字段名转为小写，然后再寻找
            columnName = columnName.toLowerCase();
            for (String key : columnNameFieldMap.keySet()) {
                if (columnName.equals(key.toLowerCase())) {
                    return columnNameFieldMap.get(key);
                }
            }

            for (String key : fieldNameFieldMap.keySet()) {
                if (columnName.equals(key.toLowerCase())) {
                    return fieldNameFieldMap.get(key);
                }
            }
        }
        return null;
    }

    private List<String> getHasValueColumnNameList(Object t) {
        return getHasValueColumnNameList(t, null);
    }

    //得到有值的列名列表,并设置参数列表
    private List<String> getHasValueColumnNameList(Object t, List<Object> argumentList) {
        //有值的字段名称列表
        List<String> hasValueColumnNameList = new LinkedList<String>();

        for (String columnName : columnNameFieldMap.keySet()) {
            Field field = columnNameFieldMap.get(columnName);

            try {
                Object value = field.get(t);
                if (value == null) {
                    continue;
                }
                hasValueColumnNameList.add(columnName);
                if (argumentList != null) {
                    argumentList.add(value);
                }
            } catch (Exception ex) {
            }
        }
        return hasValueColumnNameList;
    }

    public SqlAndArgumentsData getInsertSqlAndArgumentData(Object t, Dialect dialect) {
        //参数列表
        List<Object> argumentList = new LinkedList<Object>();
        List<String> hasValueColumnNameList = getHasValueColumnNameList(t, argumentList);

        StringBuilder sb = new StringBuilder();
        sb.append("INSERT INTO ").append(dialect.addIdentifierSeparatingChar(tableName)).append("(");
        for (int i = 0; i <= hasValueColumnNameList.size() - 1; i++) {
            if (i != 0) {
                sb.append(", ");
            }
            sb.append(dialect.addIdentifierSeparatingChar(hasValueColumnNameList.get(i)));
        }
        sb.append(") VALUES (");
        for (int i = 0; i <= hasValueColumnNameList.size() - 1; i++) {
            if (i != 0) {
                sb.append(", ");
            }
            sb.append("?");
        }
        sb.append(")");
        String sql = sb.toString();
        SqlAndArgumentsData data = new SqlAndArgumentsData(sql);
        data.setArgumentList(argumentList);
        return data;
    }

    private List<Object> getFieldValueListByColumnNameList(Object t, List<String> columnNameList) {
        List<Object> returnList = new ArrayList<Object>(columnNameList.size());
        for (String columnName : columnNameList) {
            Field field = columnNameFieldMap.get(columnName);
            try {
                returnList.add(field.get(t));
            } catch (Exception ex) {
            }
        }
        return returnList;
    }

    public SqlAndArgumentsData getBatchInsertSqlAndArgumentData(List<Object> tList, Dialect dialect) {
        //参数列表
        List<List<Object>> argumentListList = new LinkedList<List<Object>>();
        List<String> hasValueColumnNameList = getHasValueColumnNameList(tList.get(0));
        //准备参数
        for (Object t : tList) {
            argumentListList.add(getFieldValueListByColumnNameList(t, hasValueColumnNameList));
        }
        //准备SQL语句
        StringBuilder sb = new StringBuilder();
        sb.append("INSERT INTO ").append(dialect.addIdentifierSeparatingChar(tableName)).append("(");
        for (int i = 0; i <= hasValueColumnNameList.size() - 1; i++) {
            if (i != 0) {
                sb.append(", ");
            }
            sb.append(dialect.addIdentifierSeparatingChar(hasValueColumnNameList.get(i)));
        }
        sb.append(") VALUES (");
        for (int i = 0; i <= hasValueColumnNameList.size() - 1; i++) {
            if (i != 0) {
                sb.append(", ");
            }
            sb.append("?");
        }
        sb.append(")");
        String sql = sb.toString();
        SqlAndArgumentsData data = new SqlAndArgumentsData(sql);
        data.setArgumentListList(argumentListList);
        return data;
    }

    /**
     * @return the fieldNameFieldMap
     */
    public Map<String, Field> getFieldNameFieldMap() {
        return fieldNameFieldMap;
    }
}
